#include "contiki.h"
#include "ffz/raft/raft-table.h"
#include "ffz/raft/raft-packet.h"
#include "ffz/raft/raft-role.h"
#include "ffz/raft/raft-log.h"
#include "ffz/ffzconfig.h"
#include "ffz/bit-ops.h"
#define DEBUG DEBUG_PRINT
#include "ffz/debug.h"
static struct uip_udp_conn *recv_conn = NULL;
static struct uip_udp_conn *send_conn = NULL;
struct process *broadcast_proc = NULL;
static uint8_t sendbuffer[50];

uint8_t *getSendBuffer()
{
    return sendbuffer;
}

void initSendConnection(struct uip_udp_conn *conn)
{
    send_conn = conn;
}
void initRecvConnection(struct uip_udp_conn *conn)
{
    recv_conn = conn;
}
void initBroadCastProcess(struct process *process)
{
    broadcast_proc = process;
}
//Sending Function
void raftPacketSendBroadcast(uint8_t *data, uint16_t datalen)
{
    raftPacketSendUnicast(data, datalen, getBroadcastAddr());
    // int i;
    // for (i = 0; i < NODE_NUM; ++i)
    // {
    //     raftPacketSendUnicast(data, datalen, &getRaftNodeTableItem(i)->ip);
    // }
    // PRINTF("raft-packet broad\n");
}
void raftPacketSendUnicast(uint8_t *data, uint16_t datalen, uip_ipaddr_t *ip)
{
    // PRINTF("debug unicast ");
    // PRINT6ADDR(ip);
    // PRINTF("\n");
    uip_udp_packet_sendto(send_conn, data, datalen, ip, UIP_HTONS(UDP_CLIENT_PORT));
}
//RPC packet
uint8_t getRPCType(const uint8_t *data)
{
    return *data;
}
//RPC Candidate request
void createRaftPacketCandidate(uint16_t term, uint16_t candidateId, uint16_t lastLogIndex, uint16_t lastLogTerm, uint8_t *data)
{
    *data = RPC_CANDIDATE;
    setUInt16(data + 1, term);
    setUInt16(data + 3, candidateId);
    setUInt16(data + 5, lastLogIndex);
    setUInt16(data + 7, lastLogTerm);
}
void extractRaftPacketCandidate(uint16_t *term, uint16_t *candidateId, uint16_t *lastLogIndex, uint16_t *lastLogTerm, const uint8_t *data)
{
    *term = getUInt16(data + 1);
    *candidateId = getUInt16(data + 3);
    *lastLogIndex = getUInt16(data + 5);
    *lastLogTerm = getUInt16(data + 7);
}
//RPC Candidate response
void createRaftPacketCandidateResp(uint16_t term, uint8_t voteGranted, uint16_t senderId, uint8_t *data)
{
    *data = RPC_CANDIDATE_RESP;
    setUInt16(data + 1, term);
    *(data + 3) = voteGranted;
    setUInt16(data + 4, senderId);
}
void extractRaftPacketCandidateResp(uint16_t *term, uint8_t *voteGranted, uint16_t *senderId, const uint8_t *data)
{
    *term = getUInt16(data + 1);
    *voteGranted = *(data + 3);
    *senderId = getUInt16(data + 4);
}
//RPC Leader request
void createRaftPacketHeartbeats(uint16_t term, uint16_t leaderId, uint16_t prevIndex, uint16_t prevTerm, uint16_t commitIndex, uint8_t hasEntry, RaftLog *entry, uint8_t *data)
{
    *data = RPC_HEARTBEATS;
    setUInt16(data + 1, term);
    setUInt16(data + 3, leaderId);
    setUInt16(data + 5, prevIndex);
    setUInt16(data + 7, prevTerm);
    setUInt16(data + 9, commitIndex);
    *(data + 11) = hasEntry;
    setUInt16(data + 12, entry->index);
    setUInt16(data + 14, entry->term);
    *(data + 16) = entry->status;
    *(data + 17) = entry->action;
}
void extractRaftPacketHeartbeats(uint16_t *term, uint16_t *leaderId, uint16_t *prevIndex, uint16_t *prevTerm, uint16_t *commitIndex, uint8_t *hasEntry, RaftLog *entry, const uint8_t *data)
{
    *term = getUInt16(data + 1);
    *leaderId = getUInt16(data + 3);
    *prevIndex = getUInt16(data + 5);
    *prevTerm = getUInt16(data + 7);
    *commitIndex = getUInt16(data + 9);
    *hasEntry = *(data + 11);
    entry->index = getUInt16(data + 12);
    entry->term = getUInt16(data + 14);
    entry->status = *(data + 16);
    entry->action = *(data + 17);
}
void createRaftPacketHeartbeatsResp(uint16_t term, uint8_t success, uint16_t matchIndex, uint16_t senderId, uint8_t *data)
{
    *data = RPC_HEARTBEATS_RESP;
    setUInt16(data + 1, term);
    *(data + 3) = success;
    setUInt16(data + 4, matchIndex);
    setUInt16(data + 6, senderId);
}
void extractRaftPacketHeartbeatsResp(uint16_t *term, uint8_t *success, uint16_t *matchIndex, uint16_t *senderId, const uint8_t *data)
{
    *term = getUInt16(data + 1);
    *success = *(data + 3);
    *matchIndex = getUInt16(data + 4);
    *senderId = getUInt16(data + 6);
}